<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>13、Reflect——静态方法</title>
	</head>
	<body>
		<script type="text/javascript">
			//二、静态方法
			//Reflect对象一共有 13 个静态方法。
			
//		    Reflect.apply(target, thisArg, args)
//		    Reflect.construct(target, args)
//		    Reflect.get(target, name, receiver)
//		    Reflect.set(target, name, value, receiver)
//		    Reflect.defineProperty(target, name, desc)
//		    Reflect.deleteProperty(target, name)
//		    Reflect.has(target, name)
//		    Reflect.ownKeys(target)
//		    Reflect.isExtensible(target)
//		    Reflect.preventExtensions(target)
//		    Reflect.getOwnPropertyDescriptor(target, name)
//		    Reflect.getPrototypeOf(target)
//		    Reflect.setPrototypeOf(target, prototype)
			
			//上面这些方法的作用，大部分与Object对象的同名方法的作用都是相同的，而且它与Proxy对象的方法是一一对应的。下面是对它们的解释。
			
			//1.Reflect.get(target,name,receiver)
			//Reflect.get方法查找并返回target对象的name属性，如果没有该属性，则返回undefined。
			var myObject = {
				foo:1,
				bar:2,
				get baz(){
					return this.foo + this.bar;
				},
			}
			console.log(Reflect.get(myObject,'foo'))  // 1
			console.log(Reflect.get(myObject,'bar'))  // 2
			console.log(Reflect.get(myObject,'baz'))  // 3
			//如果name属性部署了读取函数（getter）,则读取函数的this绑定receiver.
			var myObject_1 = {
				foo:1,
				bar:2,
				get baz(){
					return this.foo + this.bar;
				}
			};
			var myReceiverObject = {
				foo:3,
				bar:7
			}
			console.log(Reflect.get(myObject_1,'baz',myReceiverObject)) //10
			//如果第一个参数不是对象，Reflect.get方法会报错。
//			Reflect.get(1,'foo') //报错 Uncaught TypeError: Reflect.get called on non-object
//			Reflect.get(false,'foo') //报错 Uncaught TypeError: Reflect.get called on non-object
			
			
			//2.Reflect.set(target,name,value,reveiver)
			//Reflect.set方法设置target对象的name属性等于value。
			var myObject_2 = {
				foo:1,
				set bar(value){
					return this.foo = value;
				}
			}
			console.log(myObject_2.foo)  //1
			Reflect.set(myObject_2,'foo',2)
			console.log(myObject_2.foo)  //2
			Reflect.set(myObject_2,'bar',3)
			console.log(myObject_2.foo)  //3
			//如果name属性设置了赋值函数，则赋值函数的this绑定receiver。
			var myObject_3 = {
				foo:4,
				set bar(value){
					return this.foo = value;
				}
			}
			var myReceiverObject = {
				foo:0,
			}
			Reflect.set(myObject_3,'bar',1,myReceiverObject);
			console.log(myObject_3.foo)   //4
			console.log(myReceiverObject.foo)  //1
			//注意，如果 Proxy 对象和 Reflect 对象联合使用，前者拦截赋值操作，后者完成赋值的默认行为，而且传入了receiver，那么Reflect.set会触发Proxy.defineProperty拦截。
			let p ={
				a:'a'
			};
			let handler = {
				set(target,key,value,receiver){
					console.log('set');
					Reflect.set(target,key,value,receiver)
				},
				defineProperty(target,key,attribute){
					console.log('defineProperty');
					Reflect.defineProperty(target,key,attribute);
				}
			}
			let obj = new Proxy(p,handler);
			obj.a = 'A';
			//set
			//defineProperty
			//上面代码中，Proxy.set拦截里面使用了Reflect.set，而且传入了receiver，导致触发Proxy.defineProperty拦截。这是因为Proxy.set的receiver参数总是指向当前的 Proxy 实例（即上例的obj），而Reflect.set一旦传入receiver，就会将属性赋值到receiver上面（即obj），导致触发defineProperty拦截。如果Reflect.set没有传入receiver，那么就不会触发defineProperty拦截。
			let p_1 = {
				a:'a'
			};
			let handler_1 = {
				set(target,key,value,receiver){
					console.log('set');
					Reflect.set(target,key,value)
				},
				defineProperty(target,key,attribute){
					console.log('defineProperty');
					Reflect.defineProperty(target,key,attribute);
				}
			};
			let obj_1 = new Proxy(p_1,handler_1)
			obj_1.a = 'A';   //set
			//如果第一个参数不是对象，Reflect.set会报错。
//			Reflect.set(1,'foo',{})  //报错  Uncaught TypeError: Reflect.set called on non-object
//			Reflect.set(false,'foo',{})   //报错  Uncaught TypeError: Reflect.set called on non-object
			
			
			//3.Reflect.has(obj,name)
			//Reflect.has方法对应name in obj里面的in运算符。
			var myObject_4 = {
				foo:1
			};
			//旧写法
			console.log('foo' in myObject_4)   //true
			//新写法
			console.log(Reflect.has(myObject_4,'foo'))    //true
			//如果第一个参数不是对象，Reflect.has和in运算符都会报错。
			
			
			//4.Reflect.deleteProperty(obj,name)
			//Reflect.deleteProperty方法等同于delete obj[name]，用于删除对象的属性。
			const myObj = {foo:'bar'};
			//旧写法
			delete myObj.foo
			//新写法
			console.log(Reflect.deleteProperty(myObj,'foo'))  //true
			//该方法返回一个布尔值。如果删除成功，或者被删除的属性不存在，返回true；删除失败，被删除的属性依然存在，返回false。
			
			
			//5.Reflect.construct(target,args)
			//Reflect.construct方法等同于new target(...args)，这提供了一种不使用new，来调用构造函数的方法。
			function Greeting(name){
				this.name = name;
			}
			//new 的写法
//			const instance = new Greeting('张三');
			//Reflect.construct的写法
			const instance = Reflect.construct(Greeting,['张三'])
			console.log(instance)  //Greeting {name: "张三"}
			
			
			//6.Reflect.getPrototypeOf(obj)
			//Reflect.getPrototypeOf方法用于读取对象的__proto__属性，对应Object.getPrototypeOf(obj)。
//			const myObj_1 = new FancyThing();
			//旧写法
//			Object.getPrototypeOf(myObj_1) === FancyThing.prototype
			//新写法
//			Reflect.getPrototypeOf(myObj_1) === FancyThing.prototype;
			//Reflect.getPrototypeOf和Object.getPrototypeOf的一个区别是，如果参数不是对象，Object.getPrototypeOf会将这个参数转为对象，然后再运行，而Reflect.getPrototypeOf会报错。
			console.log(Object.getPrototypeOf(1))
			//Number {constructor: function, toExponential: function, toFixed: function, toPrecision: function, toString: function…}
//			console.log(Reflect.getPrototypeOf(1)) //Uncaught TypeError: Reflect.getPrototypeOf called on non-object
			
			
			//7.Reflect.setPrototypeOf(obj,newProto)
			//Reflect.setPrototypeOf方法用于设置对象的__proto__属性，返回第一个参数对象，对应Object.setPrototypeOf(obj, newProto)。
//			const myObj_1 = new FancyThing();
//			//旧写法
//			Object.setPrototypeOf(myObj_1,OtherThing.prototype)
//			//新写法
//			Object.setPrototypeOf(myObj_1,OtherThing.prototype)

			//如果第一个参数不是对象，Object.setPrototypeOf会返回第一个参数本身，而Reflect.setPrototypeOf会报错。
			console.log(Object.setPrototypeOf(1,{}))  //1
//			Reflect.setPrototypeOf(1,{}) //Uncaught TypeError: Reflect.setPrototypeOf called on non-object

			//如果第一个参数是undefined或null，Object.setPrototypeOf和Reflect.setPrototypeOf都会报错。
//			Object.setPrototypeOf(null,{})//// TypeError: Object.setPrototypeOf called on null or undefined
//			Reflect.setPrototypeOf(null,{})  //// TypeError: Reflect.setPrototypeOf called on non-object
			
			
			//8.Reflect.apply(func,thisArg,args)
			//Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args)，用于绑定this对象后执行给定函数。
			//一般来说，如果要绑定一个函数的this对象，可以这样写fn.apply(obj, args)，但是如果函数定义了自己的apply方法，就只能写成Function.prototype.apply.call(fn, obj, args)，采用Reflect对象可以简化这种操作。
			const ages = [11,33,12,54,18,96];
			//旧写法
			const youngest = Math.min.apply(Math,ages);
			const oldest = Math.max.apply(Math,ages);
			const type = Object.prototype.toString.call(youngest)
			//新写法
			const youngest_1 = Reflect.apply(Math.min,Math,ages);
			const oldest_1 = Reflect.apply(Math.max,Math,ages);
			const type_1 = Reflect.apply(Object.prototype.toString,youngest_1,[])
			
			
			//9.Reflect.defineProperty(target,propertyKey,attributes)
			//Reflect.defineProperty方法基本等同于Object.defineProperty，用来为对象定义属性。未来，后者会被逐渐废除，请从现在开始就使用Reflect.defineProperty代替它。
			function MyDate(){
				
			}
			//旧写法
			Object.defineProperty(MyDate,'now',{
				value:() => Date.now()
			});
			//新写法
			Reflect.defineProperty(MyDate,'now',{
				value:() => Date.now()
			})
			//如果Reflect.defineProperty的第一个参数不是对象，就会抛出错误，比如Reflect.defineProperty(1, 'foo')。
			
			
			//10.Reflect.getOwnPropertyDescriptor(target,propertyKey)
			//Reflect.getOwnPropertyDescriptor基本等同于Object.getOwnPropertyDescriptor，用于得到指定属性的描述对象，将来会替代掉后者。
			var myObject_5 = {};
			Object.defineProperty(myObject_5,'hidden',{
				value:true,
				enumerable:false,
			})
			//旧写法
			var theDescriptor = Object.getOwnPropertyDescriptor(myObject_5,'hidden');
			//新写法
			var theDescriptor_1 = Reflect.getOwnPropertyDescriptor(myObject_5,'hidden')
			//Reflect.getOwnPropertyDescriptor和Object.getOwnPropertyDescriptor的一个区别是，如果第一个参数不是对象，Object.getOwnPropertyDescriptor(1, 'foo')不报错，返回undefined，而Reflect.getOwnPropertyDescriptor(1, 'foo')会抛出错误，表示参数非法。
			
			
			//11.Reflect.isExtensible(target)
			//Reflect.isExtensible方法对应Object.isExtensible，返回一个布尔值，表示当前对象是否可扩展。
			const myObject_6 = {};
			//旧写法 
			console.log(Object.isExtensible(myObject_6))  //true
			//新写法
			console.log(Reflect.isExtensible(myObject_6))  //true
			//如果参数不是对象，Object.isExtensible会返回false，因为非对象本来就是不可扩展的，而Reflect.isExtensible会报错。
			Object.isExtensible(1)  //false
//			Reflect.isExtensible(1)  //Uncaught TypeError: Reflect.isExtensible called on non-object
			
			
			//12.Reflect.preventExtensions(target)
			//Reflect.preventExtensions对应Object.preventExtensions方法，用于让一个对象变为不可扩展。它返回一个布尔值，表示是否操作成功。
			var myObject_7 = {};
			//旧写法
			console.log(Object.preventExtensions(myObject_7))  //Object {}
			//新写法
			console.log(Reflect.preventExtensions(myObject_7))  //true
			//如果参数不是对象，Object.preventExtensions在 ES5 环境报错，在 ES6 环境返回传入的参数，而Reflect.preventExtensions会报错。
			//ES5环境
			Object.preventExtensions(1) //报错
			//ES6 环境
			Object.preventExtensions(1)  //1
			//新写法
//			Reflect.preventExtensions(1)  //报错
			
			
			//13.Reflect.ownKeys(target)
			//Reflect.ownKeys方法用于返回对象的所有属性，基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和。
			var myObject_8 = {
				foo:1,
				bar:2,
				[Symbol.for('baz')]:3,
				[Symbol.for('bing')]:4
			};
			//旧写法
			console.log(Object.getOwnPropertyNames(myObject_8))   // ["foo", "bar"]
			console.log(Object.getOwnPropertySymbols(myObject_8))  //[Symbol(baz), Symbol(bing)]
			
			//新写法
			console.log(Reflect.ownKeys(myObject_8))  //["foo", "bar", Symbol(baz), Symbol(bing)]
			
		</script>
	</body>
</html>
